#include <tinx/device.h>
#include <tinx/task.h>
#include <tinx/types.h>
#include <tinx/vsprintf.h>
#include <tinx/assert.h>
#include <tinx/string.h>
#include <tinx/arena.h>
#include <tinx/list.h>

#define DEVICE_NR 32

static device_t device_table[DEVICE_NR];

device_t *get_free_device()
{
    device_t *device;
    for (int i = 0; i < DEVICE_NR; i++)
    {
        device = &device_table[i];
        if (device->type == DEV_TYPE_NULL)
        {
            return device;
        }
    }
    panic("No more free devices");
}

dev_t device_find(flag_t flag, int idx)
{
    for (int i = 0; i < DEVICE_NR; i++)
    {
        device_t *device = device_table + i;
        if (device->flag == flag)
            idx--;
        if (idx == 0)
            return device->dev;
    }
    return EOF;
}

device_t *device_get(dev_t dev)
{
    device_t *device = device_table + dev;
    assert(device->type != DEV_TYPE_NULL);
    return device;
}

static char buf[128];

// 得到存储设备的名称
char *device_get_name(dev_t parent)
{
    char *name = buf;
    idx_t idx = 0;
    if (parent == EOF)
        goto disk;

    device_t *pardev = device_get(parent);
    device_t *device = NULL;

    for (size_t i = 0; i < DEVICE_NR; i++)
    {
        device = &device_table[i];
        if (!device)
            continue;
        if (device->parent == parent)
            idx++;
    }
    sprintf(name, "%s%d", pardev->name, idx + 1);

    return name;

disk:
    for (size_t i = 0; i < DEVICE_NR; i++)
    {
        device = &device_table[i];
        if (!device)
            continue;
        if (device->flag == DEV_FLAG_DISK)
            idx++;
    }
    sprintf(name, "sd%c", idx + 'a');

    return name;
}

dev_t device_create(
    int type, flag_t flag, dev_t parent, char *name, void *ptr,
    void *write, void *read, void *ioctl)
{
    device_t *device = get_free_device();
    if (!name)
    {
        name = device_get_name(parent);
    }
    strcpy(device->name, name);
    device->type = type;
    device->flag = flag;
    device->ptr = ptr;
    device->parent = parent;
    device->write = write;
    device->read = read;
    device->ioctl = ioctl;
    device->direct = 0;
    list_init(&device->request_list);
    return device->dev;
}

int device_write(dev_t dev, void *data, int count, int offset, flag_t flags)
{
    device_t *device = device_get(dev);
    if (!device->write)
    {
        return EOF;
    }
    return device->write(device->ptr, data, count, offset, flags);
}

int device_read(dev_t dev, void *data, int count, int offset, flag_t flags)
{
    device_t *device = device_get(dev);
    if (!device->read)
    {
        return EOF;
    }
    return device->read(device->ptr, data, count, offset, flags);
}

int device_ioctl(dev_t dev, int cmd, int arg)
{
    device_t *device = device_get(dev);
    if (!device->ioctl)
    {
        return EOF;
    }
    return device->ioctl(device->ptr, cmd, arg);
}

typedef struct request_t
{
    dev_t dev;
    u32 cmd;
    u32 idx;
    u32 sectors;
    int flags;
    void *data;
    task_t *task;
    list_node_t node;
} request_t;

int do_request(request_t *req)
{
    switch (req->cmd)
    {
    case REQ_READ:
        return device_read(req->dev, req->data, req->sectors, req->idx, req->flags);
    case REQ_WRITE:
        return device_write(req->dev, req->data, req->sectors, req->idx, req->flags);
    }
    return EOF;
}

// 获得下一个请求
static request_t *request_nextreq(device_t *device, request_t *req)
{
    list_t *list = &device->request_list;

    if (device->direct == DIRECT_UP && req->node.next == &list->tail)
    {
        device->direct = DIRECT_DOWN;
    }
    else if (device->direct == DIRECT_DOWN && req->node.prev == &list->head)
    {
        device->direct = DIRECT_UP;
    }

    void *next = NULL;
    if (device->direct == DIRECT_UP)
    {
        next = req->node.next;
    }
    else
    {
        next = req->node.prev;
    }

    if (next == &list->head || next == &list->tail)
    {
        return NULL;
    }

    return element_entry(request_t, node, next);
}

int device_request(dev_t dev, int cmd, void *data, u32 sectors, idx_t idx, flag_t flags)
{
    device_t *device = device_get(dev);
    if (device->parent != -1)
    {
        idx += device_ioctl(dev, DEV_CMD_SECTORS_START, 0);
        dev = device->parent;
        device = device_get(dev);
    }

    task_t *task = running_task();

    request_t *req = (request_t *)kmalloc(sizeof(request_t));
    memset(req, 0, sizeof(request_t));

    req->cmd = cmd;
    req->data = data;
    req->idx = idx;
    req->sectors = sectors;
    req->dev = dev;
    req->flags = flags;

    list_t *list = &device->request_list;

    bool empty = list_empty(list);

    list_insert_sort(&device->request_list, &req->node, element_node_offset(request_t, node, idx));

    if (!empty)
    {
        req->task = task;
        task_block(task, TASK_BLOCKED);
    }

    int ret = do_request(req);

    request_t *nextreq = request_nextreq(device, req);

    list_remove(&req->node);
    kfree(req);

    if (nextreq)
    {
        task_unblock(nextreq->task, 0);
    }

    return ret;
}

void device_init()
{
    device_t *device;
    for (int i = 0; i < DEVICE_NR; i++)
    {
        device = &device_table[i];
        device->dev = i;
        device->type = DEV_TYPE_NULL;
    }
}